package com.atguigu.tingshu.order.service.impl;

import com.alibaba.fastjson.JSON;
import com.atguigu.tingshu.account.client.UserAccountFeignClient;
import com.atguigu.tingshu.album.client.AlbumInfoFeignClient;
import com.atguigu.tingshu.album.client.TrackInfoFeignClient;
import com.atguigu.tingshu.common.constant.KafkaConstant;
import com.atguigu.tingshu.common.constant.SystemConstant;
import com.atguigu.tingshu.common.execption.GuiguException;
import com.atguigu.tingshu.common.result.Result;
import com.atguigu.tingshu.common.result.ResultCodeEnum;
import com.atguigu.tingshu.common.service.KafkaService;
import com.atguigu.tingshu.model.album.AlbumInfo;
import com.atguigu.tingshu.model.album.TrackInfo;
import com.atguigu.tingshu.model.order.OrderDerate;
import com.atguigu.tingshu.model.order.OrderDetail;
import com.atguigu.tingshu.model.order.OrderInfo;
import com.atguigu.tingshu.model.user.VipServiceConfig;
import com.atguigu.tingshu.order.helper.SignHelper;
import com.atguigu.tingshu.order.mapper.OrderDerateMapper;
import com.atguigu.tingshu.order.mapper.OrderDetailMapper;
import com.atguigu.tingshu.order.mapper.OrderInfoMapper;
import com.atguigu.tingshu.order.service.OrderDetailService;
import com.atguigu.tingshu.order.service.OrderInfoService;
import com.atguigu.tingshu.user.client.UserInfoFeignClient;
import com.atguigu.tingshu.vo.account.AccountLockResultVo;
import com.atguigu.tingshu.vo.account.AccountLockVo;
import com.atguigu.tingshu.vo.order.OrderDerateVo;
import com.atguigu.tingshu.vo.order.OrderDetailVo;
import com.atguigu.tingshu.vo.order.OrderInfoVo;
import com.atguigu.tingshu.vo.order.TradeVo;
import com.atguigu.tingshu.vo.user.UserInfoVo;
import com.atguigu.tingshu.vo.user.UserPaidRecordVo;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import lombok.extern.slf4j.Slf4j;
import org.redisson.api.RBlockingDeque;
import org.redisson.api.RDelayedQueue;
import org.redisson.api.RedissonClient;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.core.script.DefaultRedisScript;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.Assert;
import org.springframework.util.CollectionUtils;

import java.math.BigDecimal;
import java.util.*;
import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors;

@Slf4j
@Service
@SuppressWarnings({"unchecked", "rawtypes"})
public class OrderInfoServiceImpl extends ServiceImpl<OrderInfoMapper, OrderInfo> implements OrderInfoService {

    @Autowired
    private OrderInfoMapper orderInfoMapper;
    @Autowired
    private UserInfoFeignClient userInfoFeignClient;
    @Autowired
    private AlbumInfoFeignClient albumInfoFeignClient;
    @Autowired
    private TrackInfoFeignClient trackInfoFeignClient;
    @Autowired
    private RedisTemplate redisTemplate;
    @Autowired
    private UserAccountFeignClient userAccountFeignClient;
    @Autowired
    private KafkaService kafkaService;
    @Autowired
    private OrderDetailMapper orderDetailMapper;
    @Autowired
    private OrderDerateMapper orderDerateMapper;
    @Autowired
    private OrderDetailService orderDetailService;
    @Autowired
    private RedissonClient redissonClient;



    @Override
    public OrderInfoVo trade(TradeVo tradeVo, Long userId) {

        OrderInfoVo orderInfoVo = new OrderInfoVo();

        //  订单原始金额
        BigDecimal originalAmount = new BigDecimal("0.00");
        //  减免总金额
        BigDecimal derateAmount = new BigDecimal("0.00");
        //  订单总价
        BigDecimal orderAmount = new BigDecimal("0.00");
        //  订单明细集合
        List<OrderDetailVo> orderDetailVoList = new ArrayList<>();
        //  订单减免明细列表
        List<OrderDerateVo> orderDerateVoList = new ArrayList<>();

        //  根据用户Id 获取到用户信息.
        Result<UserInfoVo> userInfoVoResult = userInfoFeignClient.getUserInfoVo(userId);
        Assert.notNull(userInfoVoResult, "返回用户结果集为空");
        UserInfoVo userInfoVo = userInfoVoResult.getData();
        Assert.notNull(userInfoVo, "用户信息为空");

        //判断购买类型
        if (tradeVo.getItemType().equals(SystemConstant.ORDER_ITEM_TYPE_ALBUM)) {
            //购买专辑
            //  查询该用户是否购买过该专辑
            Result<Boolean> isPaidAlbumResult = this.userInfoFeignClient.isPaidAlbum(tradeVo.getItemId());
            Assert.notNull(isPaidAlbumResult,"购买专辑结果集为空");
            Boolean result = isPaidAlbumResult.getData();

            //  判断当前专辑是否参与折扣。
            Result<AlbumInfo> albumInfoResult = albumInfoFeignClient.getAlbumInfo(tradeVo.getItemId());
            Assert.notNull(albumInfoResult, "专辑结果集为空");
            AlbumInfo albumInfo = albumInfoResult.getData();
            Assert.notNull(albumInfo, "专辑信息为空");

            if (result) {
                throw new GuiguException(ResultCodeEnum.REPEAT_BUY_ERROR);
            }else {
                //没有购买过说明第一次购买专辑,需要计算出各种金额，以及订单明细与减免明细.

                //判断当前这个用户是否是vip;
                if (userInfoVo.getIsVip() == 0) {
                    //  订单原始金额.
                    originalAmount = albumInfo.getPrice();
                    //判断是否打折 不是vip
                    if (albumInfo.getDiscount().compareTo(new BigDecimal("-1")) != 0) {
                        //  1000*(10-8.8)/10=120 优惠券金额
                        derateAmount = originalAmount.multiply(new BigDecimal("10").subtract(albumInfo.getVipDiscount())).divide(new BigDecimal("10"), 2, BigDecimal.ROUND_HALF_UP);
                    }
                    //订单总价
                    orderAmount = originalAmount.subtract(derateAmount);
                }else {
                        //  订单原始金额.
                        originalAmount = albumInfo.getPrice();
                        //  判断是否打折
                        if (albumInfo.getVipDiscount().compareTo(new BigDecimal("-1")) != 0) {
                            //  1000*(10-8.8)/10=120 优惠券金额
                            derateAmount = originalAmount.multiply(new BigDecimal("10").subtract(albumInfo.getVipDiscount())).divide(new BigDecimal("10"), 2, BigDecimal.ROUND_HALF_UP);
                        }
                        //  订单总价
                        orderAmount = originalAmount.subtract(derateAmount);
                }
            }

            //  订单明细：
            OrderDetailVo orderDetailVo = new OrderDetailVo();
            orderDetailVo.setItemId(tradeVo.getItemId());
            orderDetailVo.setItemPrice(orderAmount);
            orderDetailVo.setItemUrl(albumInfo.getCoverUrl());
            orderDetailVo.setItemName(albumInfo.getAlbumTitle());
            orderDetailVoList.add(orderDetailVo);

            //  减免明细
            if (derateAmount.compareTo(new BigDecimal("0.00")) != 0) {
                OrderDerateVo orderDerateVo = new OrderDerateVo();
                orderDerateVo.setDerateAmount(derateAmount);
                orderDerateVo.setDerateType(SystemConstant.ORDER_DERATE_ALBUM_DISCOUNT);
                orderDerateVo.setRemarks("专辑折扣");
                orderDerateVoList.add(orderDerateVo);
            }

        }else if (tradeVo.getItemType().equals(SystemConstant.ORDER_ITEM_TYPE_TRACK)){
            //购买声音
            //  判断用户购买的声音集数，如果<0 说明用户篡改数据.
            if (tradeVo.getTrackCount() < 0) {
                throw new GuiguException(ResultCodeEnum.DATA_ERROR);
            }

            //  购买集数 >=0; 等于0的时候购买本集 >0的时候购买了多集.
            Result<List<TrackInfo>> trackInfoListResult = trackInfoFeignClient.findPaidTrackInfoList(tradeVo.getItemId(), tradeVo.getTrackCount());
            Assert.notNull(trackInfoListResult, "购买声音集合列表为空");
            List<TrackInfo> trackInfoList = trackInfoListResult.getData();
            Assert.noNullElements(trackInfoList, "购买声音集合为空");
            Result<AlbumInfo> albumInfoResult = this.albumInfoFeignClient.getAlbumInfo(trackInfoList.get(0).getAlbumId());
            Assert.notNull(albumInfoResult, "专辑信息为空");
            AlbumInfo albumInfo = albumInfoResult.getData();
            Assert.notNull(albumInfo, "专辑对象为空");

            //  赋值订单金额；原始金额[单价=albumInfo.price]，订单金额.声音不包含折扣
            originalAmount = tradeVo.getTrackCount() != 0 ? albumInfo.getPrice().multiply(new BigDecimal(String.valueOf(tradeVo.getTrackCount()))) : albumInfo.getPrice();
            orderAmount = originalAmount;
            //  赋值订单明细，没有减免明细
            orderDetailVoList = trackInfoList.stream().map(trackInfo -> {
                //  创建对象
                OrderDetailVo orderDetailVo = new OrderDetailVo();
                //  赋值：
                orderDetailVo.setItemPrice(albumInfo.getPrice());
                orderDetailVo.setItemName(trackInfo.getTrackTitle());
                orderDetailVo.setItemUrl(trackInfo.getCoverUrl());
                //  为什么不是itemId;
                orderDetailVo.setItemId(trackInfo.getId());
                //  返回对象
                return orderDetailVo;
            }).collect(Collectors.toList());

        }else {
            //  购买vip {"itemType":"1003","itemId":2}
            //  赋值：金额+订单明细+减免明细
            Long vipServiceConfigId = tradeVo.getItemId();

            Result<VipServiceConfig> vipServiceConfigResult = this.userInfoFeignClient.getVipServiceConfig(vipServiceConfigId);
            //  判断
            Assert.notNull(vipServiceConfigResult, "vip服务配置信息结果集为空");
            VipServiceConfig vipServiceConfig = vipServiceConfigResult.getData();
            Assert.notNull(vipServiceConfig, "vip服务配置对象为空");

            //  获取原始价格：
            originalAmount = vipServiceConfig.getPrice();
            //  实际价格
            orderAmount = vipServiceConfig.getDiscountPrice();
            //  减免金额
            derateAmount = originalAmount.subtract(orderAmount);

            //  订单明细
            OrderDetailVo orderDetailVo = new OrderDetailVo();
            orderDetailVo.setItemName(vipServiceConfig.getName());
            orderDetailVo.setItemId(tradeVo.getItemId());
            orderDetailVo.setItemUrl(vipServiceConfig.getImageUrl());
            orderDetailVo.setItemPrice(orderAmount);
            orderDetailVoList.add(orderDetailVo);
            //  订单减免明细
            if (derateAmount.compareTo(new BigDecimal("0.00")) != 0) {
                OrderDerateVo orderDerateVo = new OrderDerateVo();
                orderDerateVo.setDerateType(SystemConstant.ORDER_DERATE_VIP_SERVICE_DISCOUNT);
                orderDerateVo.setDerateAmount(derateAmount);
                orderDerateVo.setRemarks("vip折扣");
                orderDerateVoList.add(orderDerateVo);
            }

        }

        //  设置tradeNo
        String tradeNo = UUID.randomUUID().toString().replaceAll("-", "");

        //  防止用户重复提交订单！
        String key = "tradeNo:" + userId + tradeNo;
        //  setnx + setex;
        this.redisTemplate.opsForValue().setIfAbsent(key, tradeNo, 120, TimeUnit.SECONDS);

        orderInfoVo.setTradeNo(tradeNo);
        orderInfoVo.setPayWay("");
        orderInfoVo.setItemType(tradeVo.getItemType());
        orderInfoVo.setOrderAmount(orderAmount);
        orderInfoVo.setOriginalAmount(originalAmount);
        orderInfoVo.setDerateAmount(derateAmount);
        orderInfoVo.setOrderDetailVoList(orderDetailVoList);
        orderInfoVo.setOrderDerateVoList(orderDerateVoList);
        //  设置时间戳：
        orderInfoVo.setTimestamp(SignHelper.getTimestamp());
        //  签名：防止用户篡改数据！
        //  OrderInfo-->String-->Map-->sign
        //  {1.除去sign参数 2.将map进行排序 3.将map中的值使用|进行拼接得到一个新的字符串 4.在新的字符串后面+盐[固定字符串] 5.将最新字符串进行md5加密返回}
        String strJson = JSON.toJSONString(orderInfoVo);
        Map map = JSON.parseObject(strJson, Map.class);
        String sign = SignHelper.getSign(map);
        orderInfoVo.setSign(sign);

        return orderInfoVo;
    }


    @Override
    public String submitOrder(OrderInfoVo orderInfoVo, Long userId) {

        //  校验签名与流水号
        Map map = JSON.parseObject(JSON.toJSONString(orderInfoVo), Map.class);
        //  初始化时orderInfoVo.setPayWay(""); 前端传递时 payWay: "1103"
        map.put("payWay", "");
        //  调用工具类方法.
        SignHelper.checkSign(map);
        //  获取页面的流水号
        String tradeNo = orderInfoVo.getTradeNo();
        //  获取redis 流水号
        String key = "tradeNo:" + userId + tradeNo;

        String sciptText = "if redis.call(\"get\",KEYS[1]) == ARGV[1]\n" +
                "then\n" +
                "    return redis.call(\"del\",KEYS[1])\n" +
                "else\n" +
                "    return 0\n" +
                "end";
        DefaultRedisScript<Long> script = new DefaultRedisScript<>();
        script.setScriptText(sciptText);
        script.setResultType(Long.class);
        Long count = (Long) this.redisTemplate.execute(script, Arrays.asList(key), tradeNo);
        if (count == 0) {
            //  抛出异常
            throw new GuiguException(ResultCodeEnum.REPEAT_BUY_ERROR);
        }
        //  声明一个orderNo 订单编号
        String orderNo = UUID.randomUUID().toString().replaceAll("-", "");

        //  判断当前的支付方式：
        if (!orderInfoVo.getPayWay().equals(SystemConstant.ORDER_PAY_ACCOUNT)) {
            //  在线支付
            this.saveOrder(orderInfoVo, userId, orderNo);
        } else {
            //  检查是否有足够的余额，需要锁定余额;
            AccountLockVo accountLockVo = new AccountLockVo();
            accountLockVo.setUserId(userId);
            accountLockVo.setAmount(orderInfoVo.getOrderAmount());
            accountLockVo.setOrderNo(orderNo);
            accountLockVo.setContent("余额支付锁定.");

            try{
                //远程调用方法。检查与锁定
                Result<AccountLockResultVo> result = userAccountFeignClient.checkAndLock(accountLockVo);

                if (result.getCode() != 200) {
                    //抛出异常
                    throw new GuiguException(result.getCode(),result.getMessage());
                }
                //保存订单，保存用户购买记录；
                this.saveOrder(orderInfoVo,userId,orderNo);

                //真正扣减金额
                kafkaService.sendMsg(KafkaConstant.QUEUE_ACCOUNT_MINUS,orderNo);

            }catch (GuiguException e){
                //解锁扣减金额
                kafkaService.sendMsg(KafkaConstant.QUEUE_ACCOUNT_UNLOCK,orderNo);
                throw new RuntimeException(e);
            }
        }

        return null;
    }

    @Transactional(rollbackFor = Exception.class)
    public void saveOrder(OrderInfoVo orderInfoVo, Long userId, String orderNo) {
        //  保存数据 order_info  order_detail  order_derate
        //  属性拷贝：
        OrderInfo orderInfo = new OrderInfo();
        BeanUtils.copyProperties(orderInfoVo, orderInfo);
        //  赋值userId
        orderInfo.setUserId(userId);
        orderInfo.setOrderTitle(orderInfoVo.getOrderDetailVoList().get(0).getItemName());
        orderInfo.setOrderNo(orderNo);
        orderInfo.setOrderStatus(SystemConstant.ORDER_STATUS_UNPAID);
        orderInfoMapper.insert(orderInfo);

        //  保存订单明细.
        List<OrderDetailVo> orderDetailVoList = orderInfoVo.getOrderDetailVoList();
        //  判断
        if (!CollectionUtils.isEmpty(orderDetailVoList)) {
            List<OrderDetail> orderDetailList = orderDetailVoList.stream().map(orderDetailVo -> {
                OrderDetail orderDetail = new OrderDetail();
                //  页面传递的 专辑Id，声音Id，vip会员Id 都在order_detail.item_id
                BeanUtils.copyProperties(orderDetailVo, orderDetail);
                orderDetail.setOrderId(orderInfo.getId());
                return orderDetail;
            }).collect(Collectors.toList());
            log.info("向订单明细表保存了数据....");

            boolean result = orderDetailService.saveBatch(orderDetailList);
            if (!result){
                throw new GuiguException(555, "保存订单明细失败");
            }
        }

        //  减免表
        List<OrderDerateVo> orderDerateVoList = orderInfoVo.getOrderDerateVoList();
        if (!CollectionUtils.isEmpty(orderDerateVoList)) {
            orderDerateVoList.stream().forEach(orderDerateVo -> {
                //  创建对象
                OrderDerate orderDerate = new OrderDerate();
                BeanUtils.copyProperties(orderDerateVo, orderDerate);
                orderDerate.setOrderId(orderInfo.getId());
                //  保存数据
                orderDerateMapper.insert(orderDerate);
            });
        }

        //  判断支付类型，记录当前用户购买记录信息.
        if (orderInfoVo.getPayWay().equals(SystemConstant.ORDER_PAY_ACCOUNT)) {
            //  余额支付：
            //  orderPaySuccess(orderNo); 区分类型：1001-专辑 1002-声音 1003-vip会员 知道具体购买的vipid, albumId,trackId; 保存到对应的表中！
            this.orderPaySuccess(orderNo);
        } else {
            //  在线支付：
            //  发送一个延迟消息,判断在规定的时间内是否进行了付款，如果没有付款则取消订单。
            this.sendDelayMessage(orderInfo.getId());
        }

    }

    private void sendDelayMessage(Long orderId) {
        try {
            //  发送延迟消息.
            RBlockingDeque<Object> blockingDeque = this.redissonClient.getBlockingDeque(KafkaConstant.QUEUE_ORDER_CANCEL);
            RDelayedQueue<Object> delayedQueue = this.redissonClient.getDelayedQueue(blockingDeque);
            delayedQueue.offer(orderId.toString(),KafkaConstant.DELAY_TIME, TimeUnit.SECONDS);
            log.info("添加延时队列成功 ，延迟时间：{}，订单id：{}", KafkaConstant.DELAY_TIME, orderId);
        } catch (Exception e) {
            log.info("添加延时队列失败 ，延迟时间：{}，订单id：{}", KafkaConstant.DELAY_TIME, orderId);
            throw new RuntimeException(e);
        }
    }

    public void orderPaySuccess(String orderNo) {
        OrderInfo orderInfo = this.getOrderInfoByOrderNo(orderNo);
        orderInfo.setOrderStatus(SystemConstant.ORDER_STATUS_PAID);
        orderInfo.setUpdateTime(new Date());
        //  更新订单状态.
        this.orderInfoMapper.updateById(orderInfo);

        //  区分购买类型 以发送消息的形式 让 用户微服务处理.
        UserPaidRecordVo userPaidRecordVo = new UserPaidRecordVo();
        userPaidRecordVo.setOrderNo(orderNo);
        userPaidRecordVo.setUserId(orderInfo.getUserId());
        userPaidRecordVo.setItemType(orderInfo.getItemType());

        //  购买的Id：专辑Id、声音Id、VIP会员Id
        List<OrderDetail> orderDetailList = orderInfo.getOrderDetailList();
        List<Long> itemIdList = orderDetailList.stream().map(OrderDetail::getItemId).collect(Collectors.toList());
        userPaidRecordVo.setItemIdList(itemIdList);
        //发送消息.
        kafkaService.sendMsg(KafkaConstant.QUEUE_USER_PAY_RECORD,JSON.toJSONString(userPaidRecordVo));

    }

    private OrderInfo getOrderInfoByOrderNo(String orderNo) {
        //  构建查询条件
        OrderInfo orderInfo = orderInfoMapper.selectOne(new LambdaQueryWrapper<OrderInfo>().eq(OrderInfo::getOrderNo, orderNo));
        //  根据订单Id 查询订单明细
//        if (null != orderInfo) {
//            orderInfo.setOrderDetailList(orderDetailMapper.selectList(new LambdaQueryWrapper<OrderDetail>().eq(OrderDetail::getOrderId,orderInfo.getId())));
//        }
        //  返回数据
        return orderInfo;
    }


    @Override
    public void orderCancel(long orderId) {
        //  更新订单状态.
        OrderInfo orderInfo = this.getById(orderId);
        //  当订单未支付的时候，取消订单.
        if (null != orderInfo && orderInfo.getOrderStatus().equals(SystemConstant.ORDER_STATUS_UNPAID)){
            //  修改订单状态
            orderInfo.setOrderStatus(SystemConstant.ORDER_STATUS_CANCEL);
            orderInfo.setUpdateTime(new Date());
            //  执行更新方法.
            orderInfoMapper.updateById(orderInfo);
        }
    }


    @Override
    public OrderInfo getOrderInfo(String orderNo) {

        //  调用方法
        OrderInfo orderInfo = this.getOrderInfoByOrderNo(orderNo);
        //  需要将支付方式由编号变为文字. 1103
        orderInfo.setPayWayName(this.getPayWayName(orderInfo.getPayWay()));
        //  返回数据
        return orderInfo;
    }

    private String getPayWayName(String payWay) {
        //  余额，微信，支付宝
        switch (payWay){
            case "1101":
                return "微信";
            case "1102":
                return "支付宝";
            case "1103":
                return "余额";
            default:
                return null;
        }
    }

    @Override
    public IPage<OrderInfo> findUserPage(Page<OrderInfo> orderInfoPage, Long userId, String orderStatus) {

        //  需要编写sql 语句； order_info order_detail;
        IPage<OrderInfo> infoIPage = orderInfoMapper.selectUserPage(orderInfoPage,userId,orderStatus);
        infoIPage.getRecords().stream().forEach(orderInfo -> {
            orderInfo.setOrderStatusName(this.getOrderStatusNameByOrderStatus(orderInfo.getOrderStatus()));
        });
        return infoIPage;

    }

    private String getOrderStatusNameByOrderStatus(String orderStatus) {
        //  根据状态获取状态名称
        switch (orderStatus){
            case "0901":
                return "未支付";
            case "0902":
                return "已支付";
            case "0903":
                return "已取消";
            default:
                return null;
        }
    }
}
